#include "uhm.hxx"

#include "uhm/wrapper/fort/util.hxx"
#include "uhm/wrapper/fort/common.hxx"
#include "uhm/wrapper/fort/linal.hxx"

#ifdef __PETSC_USE__
#include "uhm.petsc.hxx"
#include "uhm/wrapper/fort/linal.petsc.hxx"
#endif

using namespace uhm;

#ifndef __ID_FORT__
#define __ID_FORT__ 1
#endif 

#define N __ID_FORT__

typedef ID_Node_H_<N>          Id_Nd;
typedef ID_Element_<1>         Id_El;


// PETSC Hybrid Solvers

// -- LU 
F_void UHM_C2F(uhm_petsc_lu_nopiv_initialize)(F_int *type, F_int *rhs, F_int *blk, 
                                               F_ptr *solver) { 
#ifdef __PETSC_USE__
  PETSC_Solver_Base_ *s 
    = new (std::nothrow) PETSC_Solver_<Id_Nd,Id_El,LU_nopiv_Linal_>
    (*type, Mat_Base_::FULL, Mat_Base_::UNSYM, *rhs, *blk);
  UHM_ERROR(s != NULL, UHM_ERROR_SOLVER_NEW);
  solver->first  = (F_long)s;
  solver->second = UHM_FORT_PETSC_LU_NOPIV;
#else
  UHM_ERROR(false, UHM_ERROR_PETSC_NOT_AVAIL);
#endif
}

F_void UHM_C2F(uhm_petsc_lu_piv_initialize)(F_int *type, F_int *rhs, F_int *blk, 
                                             F_ptr *solver) { 
#ifdef __PETSC_USE__
  PETSC_Solver_Base_ *s
    = new (std::nothrow) PETSC_Solver_<Id_Nd,Id_El,LU_piv_Linal_>
    (*type, Mat_Base_::FULL, Mat_Base_::UNSYM, *rhs, *blk);
  UHM_ERROR(s != NULL, UHM_ERROR_SOLVER_NEW);
  solver->first  = (F_long)s;
  solver->second = UHM_FORT_PETSC_LU_PIV;
#else
  UHM_ERROR(false, UHM_ERROR_PETSC_NOT_AVAIL);
#endif
}

F_void UHM_C2F(uhm_petsc_lu_incpiv_initialize)(F_int *type, F_int *rhs, F_int *blk, 
                                               F_ptr *solver) { 
#ifdef __PETSC_USE__
  PETSC_Solver_Base_ *s
    = new (std::nothrow) PETSC_Solver_<Id_Nd,Id_El,LU_incpiv_Linal_>
    (*type, Mat_Base_::FULL, Mat_Base_::UNSYM, *rhs, *blk);
  UHM_ERROR(s != NULL, UHM_ERROR_SOLVER_NEW);
  solver->first  = (F_long)s;
  solver->second = UHM_FORT_PETSC_LU_INCPIV;
#else
  UHM_ERROR(false, UHM_ERROR_PETSC_NOT_AVAIL);
#endif
}

// -- LDL
F_void UHM_C2F(uhm_petsc_ldl_nopiv_initialize)(F_int *type, F_int *rhs, F_int *blk,
                                               F_ptr *solver) {
#ifdef __PETSC_USE__
  PETSC_Solver_Base_ *s
    = new (std::nothrow) PETSC_Solver_<Id_Nd,Id_El,LDL_nopiv_Linal_>
    (*type, Mat_Base_::LOWER_TRIANGULAR, Mat_Base_::SYM, *rhs, *blk);
  UHM_ERROR(s != NULL, UHM_ERROR_SOLVER_NEW);
  solver->first  = (F_long)s;
  solver->second = UHM_FORT_PETSC_LDL_NOPIV;
#else
  UHM_ERROR(false, UHM_ERROR_PETSC_NOT_AVAIL);
#endif

}

F_void UHM_C2F(uhm_petsc_ldl_piv_initialize)(F_int *type, F_int *rhs, F_int *blk,
                                             F_ptr *solver) {
#ifdef __PETSC_USE__
  PETSC_Solver_Base_ *s
    = new (std::nothrow) PETSC_Solver_<Id_Nd,Id_El,LDL_piv_Linal_>
    (*type, Mat_Base_::LOWER_TRIANGULAR, Mat_Base_::SYM, *rhs, *blk);
  UHM_ERROR(s != NULL, UHM_ERROR_SOLVER_NEW);
  solver->first  = (F_long)s;
  solver->second = UHM_FORT_PETSC_LDL_PIV;
#else
  UHM_ERROR(false, UHM_ERROR_PETSC_NOT_AVAIL);
#endif
}

F_void UHM_C2F(uhm_petsc_ldl_incpiv_initialize)(F_int *type, F_int *rhs, F_int *blk,
                                                F_ptr *solver) {
#ifdef __PETSC_USE__
  PETSC_Solver_Base_ *s
    = new (std::nothrow) PETSC_Solver_<Id_Nd,Id_El,LDL_incpiv_Linal_>
    (*type, Mat_Base_::LOWER_TRIANGULAR, Mat_Base_::SYM, *rhs, *blk);
  UHM_ERROR(s != NULL, UHM_ERROR_SOLVER_NEW);
  solver->first  = (F_long)s;
  solver->second = UHM_FORT_PETSC_LDL_INCPIV;
#else
  UHM_ERROR(false, UHM_ERROR_PETSC_NOT_AVAIL);
#endif
}

// -- CHOL
F_void UHM_C2F(uhm_petsc_chol_nopiv_initialize)(F_int *type, F_int *rhs, F_int *blk,
                                                F_ptr *solver) {
#ifdef __PETSC_USE__
  PETSC_Solver_Base_ *s
    = new (std::nothrow) PETSC_Solver_<Id_Nd,Id_El,CHOL_nopiv_Linal_>
    (*type, Mat_Base_::LOWER_TRIANGULAR, Mat_Base_::SYM, *rhs, *blk);
  UHM_ERROR(s != NULL, UHM_ERROR_SOLVER_NEW);
  solver->first  = (F_long)s;
  solver->second = UHM_FORT_PETSC_CHOL_NOPIV;
#else
  UHM_ERROR(false, UHM_ERROR_PETSC_NOT_AVAIL);
#endif
}

F_void UHM_C2F(uhm_petsc_chol_piv_initialize)(F_int *type, F_int *rhs, F_int *blk,
                                              F_ptr *solver) {
#ifdef __PETSC_USE__
  PETSC_Solver_Base_ *s
    = new (std::nothrow) PETSC_Solver_<Id_Nd,Id_El,CHOL_piv_Linal_>
    (*type, Mat_Base_::LOWER_TRIANGULAR, Mat_Base_::SYM, *rhs, *blk);
  UHM_ERROR(s != NULL, UHM_ERROR_SOLVER_NEW);
  solver->first  = (F_long)s;
  solver->second = UHM_FORT_PETSC_CHOL_PIV;
#else
  UHM_ERROR(false, UHM_ERROR_PETSC_NOT_AVAIL);
#endif 
}

F_void UHM_C2F(uhm_petsc_chol_incpiv_initialize)(F_int *type, F_int *rhs, F_int *blk,
                                                 F_ptr *solver) {
#ifdef __PETSC_USE__
  PETSC_Solver_Base_ *s
    = new (std::nothrow) PETSC_Solver_<Id_Nd,Id_El,CHOL_incpiv_Linal_>
    (*type, Mat_Base_::LOWER_TRIANGULAR, Mat_Base_::SYM, *rhs, *blk);
  UHM_ERROR(s != NULL, UHM_ERROR_SOLVER_NEW);
  solver->first  = (F_long)s;
  solver->second = UHM_FORT_PETSC_CHOL_INCPIV;
#else
  UHM_ERROR(false, UHM_ERROR_PETSC_NOT_AVAIL);
#endif
}

// PETSC Setting
F_void UHM_C2F(uhm_petsc_assemble)(F_ptr *solver) {
#ifdef __PETSC_USE__
  PETSC_Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PETSC_PTR(s,Id_Nd,Id_El,solver);
  s->assemble();
#else
  UHM_ERROR(false, UHM_ERROR_PETSC_NOT_AVAIL);
#endif
}

F_void UHM_C2F(uhm_petsc_set_alloc_scale)(F_ptr *solver, F_int *alloc) {
#ifdef __PETSC_USE__
  PETSC_Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PETSC_PTR(s,Id_Nd,Id_El,solver);
  s->set_alloc_scale(*alloc);
#else
  UHM_ERROR(false, UHM_ERROR_PETSC_NOT_AVAIL);
#endif
}

F_void UHM_C2F(uhm_petsc_set_tolerence)(F_ptr *solver, F_double *rtol, F_double *atol) {
#ifdef __PETSC_USE__
  PETSC_Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PETSC_PTR(s,Id_Nd,Id_El,solver);
  s->set_tolerence(*rtol, *atol);
#else
  UHM_ERROR(false, UHM_ERROR_PETSC_NOT_AVAIL);
#endif
}

F_void UHM_C2F(uhm_petsc_set_pctype)(F_ptr *solver, F_char *pctype) {
#ifdef __PETSC_USE__
  PETSC_Solver_Base_ *s;
  UHM_FORT_SOLVER_LINAL_PETSC_PTR(s,Id_Nd,Id_El,solver);
  s->set_pctype(pctype);
#else
  UHM_ERROR(false, UHM_ERROR_PETSC_NOT_AVAIL);
#endif
}
